package at.pervasive.mp3player;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;

public class Mp3Player implements Runnable, BasicPlayerListener
{

	private final BasicPlayer player;
	private final List<File> mp3Files;
	private boolean isPlaying;
	private boolean isPaused;
	private boolean isMute;
	private final List<Mp3PlayerListener> listeners;
	private int currentIndex;

	public boolean isPlaying()
	{
		return isPlaying;
	}

	public Mp3Player()
	{
		super();
		mp3Files = new LinkedList<File>();
		listeners = new LinkedList<Mp3PlayerListener>();

		player = new BasicPlayer();
		player.addBasicPlayerListener(this);

		new Thread(this).start();
	}

	public void add(List<File> files)
	{
		mp3Files.addAll(files);
		for (Mp3PlayerListener listener : listeners)
		{
			listener.addedItems(files);
		}
	}

	public void addMp3PlayerListener(Mp3PlayerListener listener)
	{
		listeners.add(listener);
	}

	public List<File> getMp3Files()
	{
		return mp3Files;
	}

	public void unmute()
	{
		isMute = false;

		try
		{
			// set volume to 1
			player.setGain(1.0);
		} catch (BasicPlayerException e)
		{
			e.printStackTrace();
		}

		System.out.println("player unmute");

		for (Mp3PlayerListener listener : listeners)
		{
			listener.unmuted();
		}
	}

	public boolean isMute()
	{
		return isMute;
	}

	public void mute()
	{
		isMute = true;

		try
		{
			// set volume to 0
			player.setGain(0.0);
		} catch (BasicPlayerException e)
		{
			e.printStackTrace();
		}

		System.out.println("player mute");

		for (Mp3PlayerListener listener : listeners)
		{
			listener.muted();
		}
	}

	private void adjustIndex()
	{
		if (currentIndex >= mp3Files.size())
			currentIndex = 0;
		else if (currentIndex < 0)
			currentIndex = mp3Files.size() - 1;
	}

	public void next()
	{
		if (isPlaying)
			playPause();

		currentIndex++;
		adjustIndex();

		for (Mp3PlayerListener listener : listeners)
		{
			listener.playingNext();
		}

		System.out.println("next");

		playFile(currentIndex);
	}

	public void playFile(int index)
	{
		currentIndex = index;
		adjustIndex();
		File selected = this.mp3Files.get(currentIndex);
		
		System.out.println("starting to play file " + selected.getName());
		
		if (isPlaying)
			playPause();
		
		try
		{
			player.open(selected);
			player.play();
		} catch (BasicPlayerException e)
		{
			e.printStackTrace();
		}

		for (Mp3PlayerListener listener : listeners)
		{
			listener.startedPlaying(selected);
		}

		isPlaying = true;
		isPaused = false;
	}

	public void playPause()
	{
		if (isPlaying)
		{
			System.out.println("pause player");

			try
			{
				player.pause();
			} catch (BasicPlayerException e)
			{
				e.printStackTrace();
			}

			// pause playing
			for (Mp3PlayerListener listener : listeners)
			{
				listener.paused();
			}

			isPaused = true;
		}
		else if (!this.mp3Files.isEmpty())
		{
			File current = this.mp3Files.get(currentIndex);

			if (isPaused)
			{
				System.out.println("unpause player");
				try
				{
					player.resume();
				} catch (BasicPlayerException e)
				{
					e.printStackTrace();
				}
			}
			else
			{
				System.out.println("start to playing file for first time");
				try
				{
					if (this.mp3Files.isEmpty())
						return;

					player.open(current);
					player.play();
				} catch (BasicPlayerException e)
				{
					e.printStackTrace();
				}
			}

			for (Mp3PlayerListener listener : listeners)
			{
				listener.startedPlaying(current);
			}
		}

		isPlaying = !isPlaying;
	}

	public void previous()
	{
		if (isPlaying)
			playPause();

		currentIndex--;
		adjustIndex();

		for (Mp3PlayerListener listener : listeners)
		{
			listener.playingPrevious();
		}

		System.out.println("previous");

		playFile(currentIndex);
	}

	public void remove(int[] selectedIndices)
	{
		List<File> removed = new LinkedList<File>();
		for (int i = selectedIndices.length - 1; i >= 0; i--)
		{
			File removedFile = mp3Files.remove(selectedIndices[i]);
			removed.add(removedFile);
		}

		for (Mp3PlayerListener listener : listeners)
		{
			listener.removedItems(removed);
		}
	}

	public void removeMp3PlayerListener(Mp3PlayerListener listener)
	{
		listeners.remove(listener);
	}

	public void shuffle()
	{
		Collections.shuffle(mp3Files);

		for (Mp3PlayerListener listener : listeners)
		{
			listener.shuffeled(mp3Files);
		}

		System.out.println("shuffeled");

		if (isPlaying)
			next();
	}

	public int getCurrentIndex()
	{
		return currentIndex;
	}

	@Override
	public void run()
	{
		while (true)
		{
			try
			{
				Socket socket = new Socket("localhost", 6668);
				InputStream inStream = socket.getInputStream();
				BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
				String line = "";
				while ((line = reader.readLine()) != null)
				{
					if (!line.contains("GESTURE_STARTED"))
						continue;

					if (line.contains("LSRS180"))
					{
						playPause();
					}
					else if (line.contains("LSRS0"))
					{
						shuffle();
					}
					else if (line.contains("LDRS"))
					{
						next();
					}
					else if (line.contains("LSRD"))
					{
						previous();
					}
					else if (line.contains("LDRU"))
					{
						mute();
					}
					else if (line.contains("LURD"))
					{
						unmute();
					}
				}
			} catch (IOException e)
			{
				// silent
			}
		}
	}

	@Override
	public void opened(Object arg0, Map arg1)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void progress(int arg0, long arg1, byte[] arg2, Map arg3)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void setController(BasicController arg0)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void stateUpdated(BasicPlayerEvent event)
	{
		if (event.getCode() == BasicPlayerEvent.STOPPED && isPlaying())
		{
			next();
		}
	}

}
